package com.beyondsoft.common.activiti.service.impl;

import com.beyondsoft.common.activiti.exception.ActivitiException;
import com.beyondsoft.common.activiti.exception.ResultCode;
import com.beyondsoft.common.activiti.model.DiagramResource;
import com.beyondsoft.common.activiti.model.TaskDefData;
import com.beyondsoft.common.activiti.model.WorkflowDefData;
import com.beyondsoft.common.activiti.service.WorkFlowDeploymentService;
import com.beyondsoft.common.activiti.support.CustomDeploySupport;
import com.beyondsoft.common.activiti.support.ShowProcessDiagramSupport;
import com.beyondsoft.common.activiti.validation.UserTaskValidator;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.BpmnAutoLayout;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.repository.Deployment;
import org.activiti.validation.ProcessValidator;
import org.activiti.validation.ProcessValidatorFactory;
import org.activiti.validation.ValidationError;
import org.activiti.validation.validator.ValidatorSet;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 流程部署
 *
 * @author zhangyan
 * @version $ Id: WorkFlowDeploymentServiceImpl.java, v 0.1 2020/6/28 14:14 zhangyan Exp $
 */
@Service
@Slf4j
public class WorkFlowDeploymentServiceImpl implements WorkFlowDeploymentService {

    @Resource
    private CustomDeploySupport customDeploySupport;

    @Resource
    private ShowProcessDiagramSupport showProcessDiagramSupport;

    @Resource
    private ProcessEngine processEngine;

    /**
     * 批量流程部署
     *
     * @param resources
     */
    @Override
    public void deploy(List<DiagramResource> resources) {
        customDeploySupport.customDeploy(resources);
    }

    /**
     * 部署
     *
     * @param name
     * @param bpmnResourceName
     * @param imgResourceName
     * @return
     */
    @Override
    public String deploy(String name, String bpmnResourceName, String imgResourceName) {
        return this.customDeploySupport.customDeploy(name, bpmnResourceName, imgResourceName);
    }

    /**
     * 创建流程并部署
     *
     * @param workflow
     */
    @Override
    public Deployment addDeployment(WorkflowDefData workflow) {
        List<TaskDefData> taskDefDataList = workflow.getTaskDefDataList();
        // 1 建立模型
        BpmnModel model = new BpmnModel();
        Process process = new Process();
        model.addProcess(process);
        process.setId(workflow.getProcDefKey());
        process.setName(workflow.getProcDefName());
        process.setDocumentation(workflow.getContent());
        //2 添加流程节点
        //2.1 开始节点
        process.addFlowElement(this.createStartEvent());
        for (int i = 1; i <= taskDefDataList.size(); i++) {
            TaskDefData step = taskDefDataList.get(i - 1);
            //2.2 审核节点
            process.addFlowElement(this.createGroupTask("userTask" + i, step));
        }
        //2.3 结束节点
        process.addFlowElement(this.createEndEvent());
        //3 连线
        this.taskLigature(taskDefDataList, process);
        //4 生成的图形信息
        new BpmnAutoLayout(model).execute();
        //5 校验
        this.userTaskValidator(model);
        //6 部署流程
        Deployment deployment = processEngine.getRepositoryService()
                .createDeployment()
                .addBpmnModel(process.getId() + ".bpmn", model)
                .name(process.getId() + "_deployment")
                .deploy();
        return deployment;
    }

    /**
     * 添加校验
     *
     * @param bpmnModel
     */
    private void userTaskValidator(BpmnModel bpmnModel) {
        ValidatorSet validatorSet = new ValidatorSet("Validate UserTask assignee");
        validatorSet.addValidator(new UserTaskValidator());
        //创建模型校验器工厂
        ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
        //创建默认模型校验器
        ProcessValidator processValidator = processValidatorFactory.createDefaultProcessValidator();
        //添加自定义模型校验器
        List<ValidatorSet> validatorSets = processValidator.getValidatorSets();
        validatorSets.add(validatorSet);

        //进行模型校验
        List<ValidationError> validate = processValidator.validate(bpmnModel);
        //如果校验错误集合长度大于1，则说明校验出错，遍历打印出错信息
        if (!CollectionUtils.isEmpty(validate)) {
            for (ValidationError validationError : validate) {
                throw new ActivitiException(ResultCode.PARAMS_EMPTY.getCode(), validationError.getProblem());
            }
        }
    }

    /**
     * 删除流程部署
     *
     * @param deploymentId
     * @return
     */
    @Override
    public void delDeployment(String deploymentId, boolean cascade) {
        processEngine.getRepositoryService()
                .deleteDeployment(deploymentId, cascade);
    }

    /**
     * 绘制连线
     *
     * @param taskDefDataList
     * @param process
     */
    private void taskLigature(List<TaskDefData> taskDefDataList, Process process) {
        for (int y = 1; y <= taskDefDataList.size(); y++) {
            TaskDefData step = taskDefDataList.get(y - 1);
            //第一个节点
            if (y == 1) {
                //开始节点和审核节点1
                process.addFlowElement(createSequenceFlow("startEvent", "userTask" + y, null, ""));
            } else {
                process.addFlowElement(createSequenceFlow("userTask" + (y - 1), "userTask" + y, "通过", "${result=='pass'}"));
            }
            //是否最后一个节点
            if (y == (taskDefDataList.size())) {
                // 审核节点到结束节点
                process.addFlowElement(createSequenceFlow("userTask" + y, "endEvent", "通过", " ${result=='pass'}"));
            }
        }
    }

    /**
     * 创建任务节点
     *
     * @param id
     * @param step
     * @return
     */
    private UserTask createGroupTask(String id, TaskDefData step) {
        UserTask userTask = new UserTask();
        userTask.setName(step.getTaskName());
        userTask.setId(id);
        //判断组任务还是个人任务
        if (StringUtils.isNotBlank(step.getGroupId())) {
            List<String> candidateGroups = new ArrayList<String>();
            candidateGroups.add("${" + step.getGroupId() + "}");
            userTask.setCandidateGroups(candidateGroups);
        } else if (Objects.isNull(step.getAssignees())) {
            userTask.setCandidateUsers(step.getAssignees());
        } else {
            userTask.setAssignee(StringUtils.isNotBlank(step.getAssignee()) ? step.getAssignee() : "${assignee}");
        }
        return userTask;
    }

    //任务节点-用户
    private UserTask createUserTask(String id, String name, String userPkno) {
        List<String> candidateUsers = new ArrayList<String>();
        candidateUsers.add(userPkno);
        UserTask userTask = new UserTask();
        userTask.setName(name);
        userTask.setId(id);
        userTask.setCandidateUsers(candidateUsers);
        return userTask;
    }

    //任务节点-锁定者
    private UserTask createAssigneeTask(String id, String name, String assignee) {
        UserTask userTask = new UserTask();
        userTask.setName(name);
        userTask.setId(id);
        userTask.setAssignee(assignee);
        return userTask;
    }

    /**
     * 连线
     *
     * @param from
     * @param to
     * @param name
     * @param conditionExpression
     * @return
     */
    private SequenceFlow createSequenceFlow(String from, String to, String name, String conditionExpression) {
        SequenceFlow flow = new SequenceFlow();
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        flow.setName(name);
        if (StringUtils.isNotBlank(conditionExpression)) {
            flow.setConditionExpression(conditionExpression);
        }
        return flow;
    }

    /**
     * 创建连线
     */
    private SequenceFlow createSequenceFlow(String id, String name, String sourceRef,
                                            String targetRef, String conditionExpression,
                                            Process process) {
        SequenceFlow sequenceFlow = new SequenceFlow(sourceRef, targetRef);
        sequenceFlow.setId(id);
        if (name != null && name != "") {
            sequenceFlow.setName(name);
        }
        sequenceFlow.setSourceFlowElement(process.getFlowElement(sourceRef));
        sequenceFlow.setTargetFlowElement(process.getFlowElement(targetRef));
        if (StringUtils.isNotBlank(conditionExpression)) {
            sequenceFlow.setConditionExpression(conditionExpression);
        }
        return sequenceFlow;
    }

    /**
     * 排他网关
     *
     * @param id
     * @param name
     * @return
     */
    private ExclusiveGateway createExclusiveGateway(String id, String name) {
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(id);
        exclusiveGateway.setName(name);
        return exclusiveGateway;
    }

    //并行网关
    private ParallelGateway createParallelGateway(String id, String name) {
        ParallelGateway gateway = new ParallelGateway();
        gateway.setId(id);
        gateway.setName(name);
        return gateway;
    }

    /**
     * 创建开始节点
     *
     * @return
     */
    private StartEvent createStartEvent() {
        StartEvent startEvent = new StartEvent();
        startEvent.setId("startEvent");
        startEvent.setName("Start");
        return startEvent;
    }

    /**
     * 创建结束节点
     *
     * @return
     */
    private EndEvent createEndEvent() {
        EndEvent endEvent = new EndEvent();
        endEvent.setId("endEvent");
        endEvent.setName("End");
        return endEvent;
    }

    /**
     * 数据库中存在的流程文档转化为模型
     *
     * @param processDefinitionId
     * @return
     */
    public WorkflowDefData queryDeployment(String processDefinitionId) {
        WorkflowDefData workflow = new WorkflowDefData();
        BpmnModel bpmnModel = processEngine.getRepositoryService()
                .getBpmnModel(processDefinitionId);
        //获取流程Process
        Process process = bpmnModel.getProcesses().get(0);
        workflow.setProcDefKey(process.getId());
        workflow.setProcDefName(process.getName());
        workflow.setContent(process.getDocumentation());
        //获取指定类型节点
        List<UserTask> flowElementsOfType = process.findFlowElementsOfType(UserTask.class);
        List<TaskDefData> taskDefDataList = new ArrayList<>();
        for (UserTask userTask : flowElementsOfType) {
            TaskDefData defData = new TaskDefData();
            defData.setTaskName(userTask.getName());
            defData.setAssignee(userTask.getAssignee());
            List<String> groups = userTask.getCandidateGroups();
            if (groups != null && groups.size() > 0) {
                defData.setGroupId(groups.get(0));
            }
            defData.setAssignees(userTask.getCandidateUsers());
            taskDefDataList.add(defData);
        }
        workflow.setTaskDefDataList(taskDefDataList);
        return workflow;
    }

    /**
     * 查看流程图
     *
     * @param instanceId
     * @param outputStream
     */
    @Override
    public void showDiagram(String instanceId, OutputStream outputStream) {
        showProcessDiagramSupport.show(instanceId, outputStream);
    }

    /**
     * 查看流程图
     *
     * @param instanceId
     */
    @Override
    public InputStream showDiagram(String instanceId) {
        return this.showProcessDiagramSupport.show(instanceId);
    }

    /**
     * 查看流程图
     *
     * @param instanceId
     */
    @Override
    public byte[] showDiagramByte(String instanceId) {
        return this.showProcessDiagramSupport.showByte(instanceId);
    }

}
